package com.wg.db.repository.code;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.lang.func.VoidFunc1;
import cn.hutool.db.Db;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wg.db.exception.DataException;
import com.wg.db.schema.SchemaHelper;
import com.wg.db.schema.SchemaTable;
import com.wg.db.sql.MySqlExpert;
import com.wg.db.sql.SqlRepository;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * MySQL 基础仓库实现（同步CRUD）
 *
 * @param <Key> 实体ID类型
 * @param <T>   实体类型
 */
public class MySqlRepository<Key extends Serializable, T> implements IRepository<Key, T> {
    private static final Log log = LogFactory.get();
    /**
     * 表结构元数据
     */
    protected final SchemaTable schemaTable;
    /**
     * 实体类型Class
     */
    protected final Class<T> entityClass;
    /**
     * 操作耗时统计
     */
    protected final TimeInterval timer = new TimeInterval(true);
    /**
     * SQL模板缓存
     */
    protected final HashMap<OperationType, String> sqlCommandMap = new HashMap<>();

    public MySqlRepository(Class<T> entityClass) {
        this.entityClass = entityClass;
        this.schemaTable = SchemaHelper.getOrCreateSchema(entityClass);
        initSqlTemplates(); // 初始化SQL模板
    }

    /**
     * 初始化SQL模板（插入、更新、删除）
     */
    private void initSqlTemplates() {
        sqlCommandMap.put(OperationType.Insert, MySqlExpert.genInsertSqlCommandText(schemaTable));
        sqlCommandMap.put(OperationType.InsertOrUpdate, MySqlExpert.genUpdateSqlCommandText(schemaTable));
        sqlCommandMap.put(OperationType.Delete, MySqlExpert.genDeleteSqlCommandText(schemaTable));
    }

    @Override
    public Optional<T> findById(Key id) {
        if (id == null) {
            log.warn("查询失败：ID为空");
            return Optional.empty();
        }

        try {
            T entity = SqlRepository.get(entityClass, schemaTable, id.toString());
            return Optional.ofNullable(entity);
        } catch (Exception e) {
            log.error("查询实体失败: id={}", id, e);
            return Optional.empty();
        }
    }

    @Override
    public List<T> findAll() {
        try {
            return SqlRepository.getAll(entityClass, schemaTable);
        } catch (Exception e) {
            log.error("查询所有实体失败", e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<T> findByGroupId(Serializable groupId) {
        if (groupId == null || schemaTable.getGroupField() == null) {
            log.warn("查询失败：组ID为空或表无分组字段");
            return new ArrayList<>();
        }

        try {
            return SqlRepository.getGroup(entityClass, schemaTable, groupId.toString());
        } catch (Exception e) {
            log.error("按组查询失败: groupId={}", groupId, e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<T> findBySql(String sql) {
        if (sql == null || sql.trim().isEmpty()) {
            log.warn("查询失败：SQL为空");
            return new ArrayList<>();
        }

        try {
            return SqlRepository.getAll(entityClass, schemaTable, sql);
        } catch (Exception e) {
            log.error("自定义SQL查询失败: sql={}", sql, e);
            return new ArrayList<>();
        }
    }

    @Override
    public void insert(T entity) {
        if (entity == null) {
            log.warn("插入失败：实体为空");
            return;
        }

        try {
            Object[] genInsertOrUpdateSqlArgs = MySqlExpert.genInsertOrUpdateSqlArgs(schemaTable, entity);
            List<Object[]> list = new ArrayList<>();
            list.add(genInsertOrUpdateSqlArgs);
            executeSql(OperationType.Insert, list);

            log.debug("插入成功: 实体={}", entity);
        } catch (Exception e) {
            log.error("插入失败: 实体={}", entity, e);
            throw new DataException("插入实体失败", e);
        }
    }

    @Override
    public void batchInsert(List<T> entities) {
        if (CollUtil.isEmpty(entities)) {
            log.warn("批量插入失败：实体列表为空");
            return;
        }

        try {
            List<Object[]> list = entities.stream()
                    .filter(Objects::nonNull)
                    .map(entity -> MySqlExpert.genInsertOrUpdateSqlArgs(schemaTable, entity))
                    .collect(Collectors.toList());

            executeSql(OperationType.Insert, list);
            log.debug("批量插入成功: 数量={}", entities.size());
        } catch (Exception e) {
            log.error("批量插入失败: 数量={}", entities.size(), e);
            throw new DataException("批量插入失败", e);
        }
    }

    @Override
    public void update(T entity) {
        if (entity == null) {
            log.warn("更新失败：实体为空");
            return;
        }

        try {
            Object[] genInsertOrUpdateSqlArgs = MySqlExpert.genInsertOrUpdateSqlArgs(schemaTable, entity);
            List<Object[]> list = new ArrayList<>();
            list.add(genInsertOrUpdateSqlArgs);
            executeSql(OperationType.InsertOrUpdate, list);
            log.debug("更新成功: 实体={}", entity);
        } catch (Exception e) {
            log.error("更新失败: 实体={}", entity, e);
            throw new DataException("更新实体失败", e);
        }
    }

    @Override
    public void batchUpdate(List<T> entities) {
        if (CollUtil.isEmpty(entities)) {
            log.warn("批量更新失败：实体列表为空");
            return;
        }

        try {
            List<Object[]> list = entities.stream()
                    .filter(Objects::nonNull)
                    .map(entity -> MySqlExpert.genInsertOrUpdateSqlArgs(schemaTable, entity))
                    .collect(Collectors.toList());

            executeSql(OperationType.InsertOrUpdate, list);
            log.debug("批量更新成功: 数量={}", entities.size());
        } catch (Exception e) {
            log.error("批量更新失败: 数量={}", entities.size(), e);
            throw new DataException("批量更新失败", e);
        }
    }

    @Override
    public void delete(T entity) {
        if (entity == null) {
            log.warn("删除失败：实体为空");
            return;
        }

        try {
            Object[] genInsertOrUpdateSqlArgs = MySqlExpert.genInsertOrUpdateSqlArgs(schemaTable, entity);
            List<Object[]> list = new ArrayList<>();
            list.add(genInsertOrUpdateSqlArgs);
            executeSql(OperationType.Delete, list);
            log.debug("删除成功: 实体={}", entity);
        } catch (Exception e) {
            log.error("删除失败: 实体={}", entity, e);
            throw new DataException("删除实体失败", e);
        }
    }

    @Override
    public void deleteById(Key id) {
        findById(id).ifPresent(this::delete);
    }

    @Override
    public void batchDelete(List<T> entities) {
        if (CollUtil.isEmpty(entities)) {
            log.warn("批量删除失败：实体列表为空");
            return;
        }

        try {
            List<Object[]> list = entities.stream()
                    .filter(Objects::nonNull)
                    .map(entity -> MySqlExpert.genInsertOrUpdateSqlArgs(schemaTable, entity))
                    .collect(Collectors.toList());

            executeSql(OperationType.Delete, list);
            log.debug("批量删除成功: 数量={}", entities.size());
        } catch (Exception e) {
            log.error("批量删除失败: 数量={}", entities.size(), e);
            throw new DataException("批量删除失败", e);
        }
    }

    @Override
    public boolean existsById(Key id) {
        return findById(id).isPresent();
    }

    @Override
    public int pendingSize() {
        return 0;
    }

    @Override
    public void close() {
    }

    private void executeSql(OperationType operationType, List<Object[]> objects) {
        timer.restart();
        try {
            schemaTable.getDb().tx((VoidFunc1<Db>) db -> {
                String string = sqlCommandMap.get(operationType);
                db.executeBatch(string, objects);
            });

            log.info("SQL执行完成: 表={}, 数量={}, 耗时={}ms",
                    schemaTable.getRealTableName(),
                    objects.size(),
                    timer.intervalMs());
        } catch (Exception e) {
            throw new DataException("数据库操作失败", e);
        }
    }
}